package com.heima.wemedia.service.impl;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.apis.article.IArticleClient;
import com.heima.common.constants.WemediaConstants;
import com.heima.common.constants.WmNewsMessageConstants;
import com.heima.common.exception.CustomException;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.dtos.AdminNewsAuthDto;
import com.heima.model.wemedia.dtos.AdminNewsDto;
import com.heima.model.wemedia.dtos.WmNewsDto;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.utils.thread.WmThreadLocalUtil;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsAutoScanService;
import com.heima.wemedia.service.WmNewsService;
import com.heima.wemedia.service.WmNewsTaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.nntp.Article;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
@Transactional
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
    /**
     * 查询文章
     * @param dto
     * @return
     */
    @Override
    public ResponseResult findAll(WmNewsPageReqDto dto) {
        // 1. 参数检验
        dto.checkParam();
        // 2. 分页查询
        IPage page = new Page(dto.getPage(), dto.getSize());
        LambdaQueryWrapper<WmNews> queryWrapper = new LambdaQueryWrapper<>();
        // 2.1 查询状态
        queryWrapper.eq(dto.getStatus()!=null, WmNews::getStatus, dto.getStatus());
        // 2.2 查询所属频道
        queryWrapper.eq(dto.getChannelId()!=null, WmNews::getChannelId, dto.getChannelId());
        // 2.3 查询关键字
        queryWrapper.like(dto.getKeyword()!=null, WmNews::getTitle, dto.getKeyword());
        // 2.4 查询时间
        queryWrapper.between(dto.getBeginPubDate()!=null&&dto.getEndPubDate()!=null, WmNews::getPublishTime, dto.getBeginPubDate(), dto.getEndPubDate());
        // 2.5 根据当前用户查询
        queryWrapper.eq(WmNews::getUserId, WmThreadLocalUtil.getUser().getId());
        // 2.6 根据发布时间倒序排序
        queryWrapper.orderByDesc(WmNews::getPublishTime);
        // 2.7 查询结果
        page(page, queryWrapper);
        // 3. 返回结果
        PageResponseResult result = new PageResponseResult(dto.getPage(), dto.getSize(), (int) page.getTotal());
        result.setData(page.getRecords());
        return result;
    }

    @Autowired
    private WmNewsAutoScanService wmNewsAutoScanService;
    @Autowired
    private WmNewsTaskService wmNewsTaskService;

    /**
     * 发布文章或保存草稿
     * @param dto
     * @return
     */
    @Override
    public ResponseResult submitNews(WmNewsDto dto) {
        // 1. 检查数据
        if(dto == null || dto.getContent() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 2. 保存或修改文章
        WmNews wmNews = new WmNews();
        // 属性拷贝, 属性名和类型相同才能拷贝
        BeanUtils.copyProperties(dto, wmNews);
        // 封面图片: List -> String
        if(dto.getImages() != null && dto.getImages().size() > 0){
            String imagesStr = StringUtils.join(dto.getImages(), ",");
            wmNews.setImages(imagesStr);
        }
        // 如果当前封面类型为自动-1
        if(dto.getType() == WemediaConstants.WM_NEWS_TYPE_AUTO){
            wmNews.setType(null);
        }
        saveOrUpdateWmNews(wmNews);
        // 3. 判断是否为草稿, 如果为草稿结束当前方法
        if(dto.getStatus() == WmNews.Status.NORMAL.getCode()){
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        }
        // 获取到文章内容中的图片信息
        List<String> materials = extractUrlInfo(dto.getContent());
        // 4. 不是草稿, 保存文章内容图片与素材的关系
        saveRelativeInfoForContent(materials, wmNews.getId());
        // 5. 不是草稿, 保存文章封面图片与素材的关系
        saveRelativeInfoForCover(dto, wmNews, materials);

        // wmNewsAutoScanService.autoScanWmNews(wmNews.getId());
        wmNewsTaskService.addNewsToTask(wmNews.getId(), wmNews.getPublishTime());

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 查看文章详情
     * @param id
     * @return
     */
    @Override
    public ResponseResult getNewsDetail(Integer id) {
        // 1. 检验参数
        if(id == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 2. 获取文章对象
        WmNews news = getById(id);
        if(news == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }
        return ResponseResult.okResult(news);
    }

    /**
     * 删除文章
     * @param id
     * @return
     */
    @Override
    public ResponseResult delNews(Integer id) {
        // 1. 检验参数
        if(id == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "文章id不可缺少");
        }
        // 2. 查询文章
        WmNews news = getById(id);
        if(news == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }
        // 3. 查询文章状态
        // 3.1 已发布, 不能删除
        if(news.getStatus() == WmNews.Status.PUBLISHED.getCode()){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "文章已发布, 不能删除");
        }
        // 3.2 草稿, 没有关系数据
        if(news.getStatus() == WmNews.Status.NORMAL.getCode()){
            // 删除文章数据
            removeById(id);
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        }
        // 4. 删除文章
        // 4.1 删除文章素材关系表中的关系数据
        LambdaQueryWrapper<WmNewsMaterial> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(WmNewsMaterial::getNewsId, id);
        wmNewsMaterialMapper.delete(queryWrapper);
        // 4.2 删除文章表中的数据
        removeById(id);
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    @Autowired
    private KafkaTemplate kafkaTemplate;

    /**
     * 文章上下架
     * @param dto
     * @return
     */
    @Override
    public ResponseResult downOrUp(WmNewsDto dto) {
        // 1. 检验参数
        // 1.0 检查文章dto是否为空
        if(dto == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "文章id不可缺少");
        }
        // 1.1 检查文章上架参数是否合法
        if(dto.getEnable() != 0 && dto.getEnable() != 1){
            // 默认上架
            dto.setEnable((short) 1);
        }
        // 2. 查询文章
        WmNews news = getById(dto.getId());
        if(news == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }
        // 3. 查询文章状态
        if(news.getStatus() != WmNews.Status.PUBLISHED.getCode()){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "当前文章不是发布状态, 不能上下架");
        }
        // 4. 上下架
        news.setEnable(dto.getEnable());
        updateById(news);

        // 5. 发送消息, 通知article修改文章的配置
        if(news.getArticleId() != null){
            HashMap<String, Object> map = new HashMap<>();
            map.put("articleId", news.getArticleId());
            map.put("enable", news.getEnable());
            kafkaTemplate.send(WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC, JSON.toJSONString(map));
        }

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    @Autowired
    private WmUserMapper wmUserMapper;

    /**
     * 后台端查询文章列表
     * @param dto
     * @return
     */
    @Override
    public ResponseResult listVo(AdminNewsAuthDto dto) {
        // 需求
        // 1. 分页查询自媒体文章
        // 2. 可以按照标题模糊查询
        // 3. 可以按照审核状态进行精确检索
        // 4. 文章查询按照创建时间倒序查询
        // 5. 注意：需要展示作者名称

        // 1. 检验参数
        dto.checkParam();

        // 2. 分页查询文章
        IPage page = new Page(dto.getPage(), dto.getSize());
        LambdaQueryWrapper<WmNews> query = new LambdaQueryWrapper<>();
        // 2.1 按照标题模糊查询
        query.like(!StringUtils.isBlank(dto.getTitle()), WmNews::getTitle, dto.getTitle());
        // 2.2 按照审核状态查询
        query.eq(dto.getStatus()!=null, WmNews::getStatus, dto.getStatus());
        // 2.3 按照创建时间倒序查询
        query.orderByDesc(WmNews::getCreatedTime);
        // 2.4 查询结果
        page(page, query);

        List<WmNews> records = page.getRecords();
        List<AdminNewsDto> collect = records.stream().map((item) -> {

            AdminNewsDto adminNewsDto = new AdminNewsDto();
            BeanUtils.copyProperties(item, adminNewsDto);

            // 作者id
            Integer userId = item.getUserId();
            // 作者名字
            String authorName = wmUserMapper.selectById(userId).getName();

            // 设置名字
            adminNewsDto.setAuthorName(authorName);
            return adminNewsDto;
        }).collect(Collectors.toList());

        // 3. 返回结果
        PageResponseResult result = new PageResponseResult(dto.getPage(), dto.getSize(), (int) page.getTotal());
        result.setData(collect);
        return result;
    }

    /**
     * 后台端查询文章详情
     * @param id
     * @return
     */
    @Override
    public ResponseResult adminGetNewsDetail(Integer id) {
        // 需求
        // 1. 可以查看文章详细内容
        // 2. 注意：需要展示作者名称


        // 1. 检验参数
        if(id == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 2. 获取文章对象
        WmNews news = getById(id);
        if(news == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }

        // 3. 设置作者名称
        AdminNewsDto adminNewsDto = new AdminNewsDto();
        BeanUtils.copyProperties(news, adminNewsDto);
        // 3.1 获取作者名称
        String authorName = wmUserMapper.selectById(news.getUserId()).getName();
        adminNewsDto.setAuthorName(authorName);

        // 4. 返回结果
        return ResponseResult.okResult(adminNewsDto);
    }

    /**
     * 后台端审核文章失败
     * @param dto
     * @return
     */
    @Override
    public ResponseResult authNewsFail(AdminNewsAuthDto dto) {
        // 需求
        // 1. 拒绝以后需要给出原因，并修改文章的状态为2
        if(dto.getMsg() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR, "需要给出拒绝原因");
        }
        WmNews wmNews = new WmNews();
        wmNews.setId(dto.getId());
        wmNews.setReason(dto.getMsg());
        wmNews.setStatus(dto.getStatus().shortValue());
        updateById(wmNews);
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    @Autowired
    private IArticleClient articleClient;

    /**
     * 后台端审核文章成功
     * @param dto
     * @return
     */
    @Override
    public ResponseResult authNewsPass(AdminNewsAuthDto dto) {
        // 需求
        // 1. 需要创建app端的文章信息，并更新自媒体文章的状态

        // 1. 更新自媒体文章状态
        WmNews wmNews = new WmNews();
        wmNews.setId(dto.getId());
        wmNews.setStatus(dto.getStatus().shortValue());
        updateById(wmNews);

        // 2. 创建app端的文章信息
        ArticleDto articleDto = new ArticleDto();
        // 2.1 获取自媒体端文章
        WmNews news = getById(dto.getId());
        wmNewsAutoScanService.saveAppArticle(news);
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 1. 若封面类型为自动, 则需设置封面类型的数据
     * 2. 保存封面图片与素材的关系
     */
    private void saveRelativeInfoForCover(WmNewsDto dto, WmNews wmNews, List<String> materials) {
        // 自动类型设置封面类型的数据
        List<String> imgsCover = dto.getImages();
        if(dto.getType() == WemediaConstants.WM_NEWS_TYPE_AUTO){
            if(materials.size() >= 3){
                wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
                imgsCover = materials.stream().limit(3).collect(Collectors.toList());
            }else if(materials.size() >= 1){
                wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
                imgsCover = materials.stream().limit(1).collect(Collectors.toList());
            }else{
                wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
            }
            // 修改文章
            if(imgsCover!=null && imgsCover.size() > 0){
                wmNews.setImages(StringUtils.join(imgsCover, ","));
            }
            updateById(wmNews);
        }
        // 保存封面图片与素材的关系
        if(imgsCover!=null && imgsCover.size() > 0){
            saveRelativeInfo(imgsCover, wmNews.getId(), WemediaConstants.WM_COVER_REFERENCE);
        }
    }

    /**
     * 处理文章内容图片与素材的关系
     * @param materials
     * @param newsId
     */
    private void saveRelativeInfoForContent(List<String> materials, Integer newsId) {
        saveRelativeInfo(materials, newsId, WemediaConstants.WM_CONTENT_REFERENCE);
    }

    @Autowired
    private WmMaterialMapper wmMaterialMapper;

    /**
     * 保存文章图片与素材的关系到数据库中
     * @param materials
     * @param newsId
     * @param type
     */
    private void saveRelativeInfo(List<String> materials, Integer newsId, Short type) {
        if(materials==null || materials.isEmpty()){
            return;
        }
        // 通过url查询id
        LambdaQueryWrapper<WmMaterial> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(WmMaterial::getUrl, materials);
        List<WmMaterial> wmMaterialList = wmMaterialMapper.selectList(queryWrapper);

        // 判断素材是否有效
        if(wmMaterialList==null || wmMaterialList.size()!=materials.size()){
            // 手动抛出异常
            // 1. 提示调用者素材失效
            // 2. 进行数据的回滚
            throw new CustomException(AppHttpCodeEnum.MATERIAL_REFERENCE_FAIL);
        }

        List<Integer> ids = wmMaterialList.stream().map(WmMaterial::getId).collect(Collectors.toList());

        wmNewsMaterialMapper.saveRelations(ids, newsId, type);
    }

    private List<String> extractUrlInfo(String content) {
        List<String> materials = new ArrayList<>();
        List<Map> maps = JSON.parseArray(content, Map.class);
        for (Map map : maps) {
            if(map.get("type").equals("image")){
                String imgUrl = (String) map.get("value");
                materials.add(imgUrl);
            }
        }
        return materials;
    }


    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;
    /**
     * 保存或修改文章
     * @param wmNews
     */
    private void saveOrUpdateWmNews(WmNews wmNews) {
        // 补全属性
        wmNews.setUserId(WmThreadLocalUtil.getUser().getId());
        wmNews.setCreatedTime(new Date());
        wmNews.setSubmitedTime(new Date());
        wmNews.setEnable((short) 1); // 默认上架

        if(wmNews.getId() == null){
            // 保存
            save(wmNews);
        }else{
            // 修改
            // 删除文章图片和素材的关系
            LambdaQueryWrapper<WmNewsMaterial> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(WmNewsMaterial::getNewsId, wmNews.getId());
            wmNewsMaterialMapper.delete(queryWrapper);
            updateById(wmNews);
        }
    }
}
